<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>WebRTC Example</title>
</head>
<body>
  <h1>WebRTC Example</h1>
  <video id="localVideo" autoplay playsinline muted></video>
  <video id="remoteVideo" autoplay playsinline></video>
  <button id="startCall">Start Call</button>

  <script>
    const signalingServer = new WebSocket(`wss://${location.host}/ws`);
    const localVideo = document.getElementById("localVideo");
    const remoteVideo = document.getElementById("remoteVideo");
    const startCallButton = document.getElementById("startCall");

    let localStream;
    let peerConnection;

    const configuration = {
      iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
    };

    signalingServer.onmessage = async (message) => {
      if(!peerConnection) return;

      try {
        const data = JSON.parse(message.data);

        console.log("Received message:", data);

        switch (data.type) {
          case "join":
            const offer = await peerConnection.createOffer();
            await peerConnection.setLocalDescription(offer);

            signalingServer.send(JSON.stringify(offer));
          case "offer":
            if (peerConnection.signalingState !== "stable") {
              console.log("Cannot process offer, signaling state is:", peerConnection.signalingState);
              return;
            }
            await peerConnection.setRemoteDescription(new RTCSessionDescription(data));
            const answer = await peerConnection.createAnswer();
            await peerConnection.setLocalDescription(answer);
            signalingServer.send(JSON.stringify(answer));
            break;

          case "answer":
            if (peerConnection.signalingState !== "have-local-offer") {
              console.log("Cannot process answer, signaling state is:", peerConnection.signalingState);
              return;
            }
            await peerConnection.setRemoteDescription(new RTCSessionDescription(data));
            break;

          case "candidate":
            if (peerConnection.signalingState === "closed") {
              console.warn("Skipping candidate, connection is closed");
              return;
            }
            await peerConnection.addIceCandidate(new RTCIceCandidate(data.candidate));
            break;

          default:
            console.warn("Unknown message type:", data.type);
        }
      } catch (error) {
        console.error("Error handling signaling message:", error);
        console.log("Stack trace:", error.stack);     // 错误堆栈
      }
    };

    async function startCall() {
      localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });
      localVideo.srcObject = localStream;

      peerConnection = new RTCPeerConnection(configuration);

      peerConnection.onicecandidate = ({ candidate }) => {
        if (candidate) {
          signalingServer.send(JSON.stringify({ type: "candidate", candidate }));
        }
      };

      peerConnection.ontrack = (event) => {
        remoteVideo.srcObject = event.streams[0];
      };

      localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));

      // Listen for connectionstatechange on the local RTCPeerConnection
      peerConnection.addEventListener('connectionstatechange', event => {
          console.log("peerConnection.connectionState: ", peerConnection.connectionState);
      });

      // Join the room
      signalingServer.send(JSON.stringify({ type: "join", join: "" }));
    }

    startCallButton.addEventListener("click", startCall);
  </script>
</body>
</html>
